package com.easyfile.easyfilemaster.aop;


import com.easyfile.easyfilemaster.entity.annotations.RateLimit;
import com.easyfile.easyfilemaster.entity.base.ResultFactory;
import com.easyfile.easyfilemaster.exception.ToRequestLimitsException;
import com.google.common.collect.Lists;
import com.google.common.util.concurrent.RateLimiter;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;

/**
 * 接口限流切面
 * @author Chenpeman
 */
@Slf4j
@Aspect
@Component
public class RateLimitAop {

    @Resource
    private RedisTemplate<String,Object> redisTemplate;

    @Resource
    private RedisScript<Long> rateLimitLua;
    /**
     * 切面
     *
     * @param joinPoint the join point
     * @return the object
     * @throws Throwable the throwable
     */
    @Around("@annotation(com.easyfile.easyfilemaster.entity.annotations.RateLimit)")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable{
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();

        //拿limit的注解
        RateLimit rateLimit = method.getAnnotation(RateLimit.class);
        if (rateLimit == null) {
            return joinPoint.proceed();
        }

        //获取请求的参数
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = requestAttributes.getRequest();
        //请求uri
        String uri = request.getRequestURI();
        //请求发起者的ip
        String ip  = request.getRemoteAddr();

        //key：根据自定义key+ip+uri进行限流
        String key=rateLimit.key()+":"+ip+":"+uri;
        int limit = rateLimit.limit();
        int expire = rateLimit.expire();

        //执行lua脚本
        Long result = redisTemplate.execute(rateLimitLua, Lists.newArrayList(key), expire, limit);

        //判断限流结果
        if (result==null||result==0){
            log.warn("IP:[{}]=>URI[{}]请求次数超过上限 {}/{}秒",ip,uri,limit,expire);
            throw new ToRequestLimitsException("请求次数超过上限");
        }

        return joinPoint.proceed();
    }

}

